package concurrecy.util;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Fat {
    private static int count = 0;
    private final int id = count ++;

    @Override
    public String toString() {
        return "Fat" + id;
    }
}

interface Generator<T> {
    T next() throws InterruptedException;
}

class ExchangerProducer<T> implements Runnable {
    private Exchanger<List<T>> exchanger;
    private Generator<T> generator;
    private List<T> tList;

    private final static int SIZE = 10;

    public ExchangerProducer(Exchanger<List<T>> exchanger, Generator<T> generator, List<T> tList) {
        this.exchanger = exchanger;
        this.generator = generator;
        this.tList = tList;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                if(!tList.isEmpty()) {
                    System.out.println("list is not empty, clear it");
                    tList.clear();
                }
                for (int i = 0; i < SIZE; i++) {
                    T next = generator.next();
                    System.out.println("produce " + next);
                    tList.add(next);
                }
                System.out.println("producer ready to swap, " + tList);
                tList = exchanger.exchange(tList);
                System.out.println("producer swap success, " + tList);
            }
        } catch(InterruptedException e) {
            System.out.println("producer interrupted");
        }
    }
}

class ExchangerConsumer<T> implements Runnable {
    private Exchanger<List<T>> exchanger;
    private List<T> tList;

    public ExchangerConsumer(Exchanger<List<T>> exchanger, List<T> tList) {
        this.exchanger = exchanger;
        this.tList = tList;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                System.out.println("consumer ready swap, " + tList);
                tList = exchanger.exchange(tList);
                System.out.println("consumer swap success, " + tList);
                Iterator<T> it = tList.iterator();
                while(it.hasNext()) {
                    System.out.println("consume " + it.next());
                    it.remove();
                }
            }
        } catch(InterruptedException e) {
            System.out.println("consumer interrupted");
        }
    }
}

public class ExchangerDemo {
    public static void main(String[] args) throws IOException {
        Exchanger<List<Fat>> exchanger = new Exchanger<>();
        List<Fat> list1 = new LinkedList<>(), list2 = new LinkedList<>();
        System.out.println("list1: " + list1);
        System.out.println("list2: " + list2);
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new ExchangerProducer<Fat>(exchanger, () -> {
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
                return Fat.class.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }, list1));
        service.execute(new ExchangerConsumer<Fat>(exchanger, list2));
        System.in.read();
        service.shutdownNow();

    }
}
